﻿using System;
using System.IO;
using System.Data;

using KSS.Generator.Configuration;
using KSS.Generator.Schema;

namespace KSS.Generator
{
    public class GeneratorHelper
    {
        /// <summary>
        /// Get property place holder string as in template file
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetPropertyPlaceHolder(string propertyName, PropertyType type)
        {
            string template = string.Empty;
            switch (type)
            {
                case PropertyType.Local:
                    template = Template.LOCAL_PROPERTY_CHARACTER + "{0}" + Template.LOCAL_PROPERTY_CHARACTER;
                    break;
                case PropertyType.Global:
                    template = Template.GLOBAL_PROPERTY_CHARACTER + "{0}" + Template.GLOBAL_PROPERTY_CHARACTER;
                    break;
                case PropertyType.Custom:
                    template = Template.CUSTOM_PROPERTY_CHARACTER + "{0}" + Template.CUSTOM_PROPERTY_CHARACTER;
                    break;
            }

            return string.Format(template, propertyName);
        }

        /// <summary>
        /// Get file name from template file (*.tpl)
        /// Format: {0}.cs
        /// </summary>
        /// <param name="templateFile">Template file name</param>
        /// <returns></returns>
        public static string GetFileNameFromat(string templateFile)
        {
            string fileName = templateFile.Substring(0, templateFile.Length - Template.TEMPLATE_FILE_EXTENSION.Length);
            int start = fileName.IndexOf("[");
            int end = fileName.IndexOf("]");

            // replace string [...] => {0}
            if (start >= 0 && end >= 0 && end > start)
            {
                fileName = fileName.Remove(start, end - start + 1);
                fileName = fileName.Insert(start, "{0}");
            }

            return fileName;
        }

        /// <summary>
        /// Get store procedure name from full name
        /// </summary>
        /// <param name="storeProcedurFullName"></param>
        /// <returns></returns>
        public static string GetStoredProcedureName(string storeProcedurFullName)
        {
            return storeProcedurFullName.Replace("dbo.", "");
        }

        /// <summary>
        /// Generate variable name from column name
        /// Format: _columnName
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public static string GeneratePrivateVariableName(string name)
        {
            return "_" + name.Substring(0, 1).ToLower() + name.Substring(1, name.Length - 1);
        }

        /// <summary>
        /// Get local variable name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string GenerateLocalVariableName(string name)
        {
            return name.Substring(0, 1).ToLower() + name.Substring(1, name.Length - 1);
        }

        /// <summary>
        /// Get global variable name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string GenerateGlobalVariableName(string name)
        {
            return name.Substring(0, 1).ToUpper() + name.Substring(1, name.Length - 1);
        }

        /// <summary>
        /// Get C# data type from column schema
        /// </summary>
        /// <param name="c">Column schema</param>
        /// <returns>C# data type</returns>
        public static string GetTypeFromColumn(Column c)
        {
            if (c == null)
            {
                return string.Empty;
            }

            string dataType = c.Type;

            if (c.CanBeNull)
            {
                dataType = GetNullableType(c.Type);
            }

            return dataType;
        }

        /// <summary>
        /// Get nullable type of a type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetNullableType(string type)
        {
            if (IsAddinNullableType(type))
            {
                //return string.Format("System.Nullable<{0}>", type);
                return type + "?";
            }

            return type;
        }

        /// <summary>
        /// Get return type of a store procedure function
        /// </summary>
        /// <param name="f"></param>
        /// <returns></returns>
        public static string GetReturnType(GeneratorContext context, Function f)
        {
            if (f == null)
            {
                return string.Empty;
            }

            string returnType = string.Empty;

            if (f.Return != null)
            {
                returnType = f.Return.Type;
            }
            else if (f.ElementType != null)
            {
                returnType = GetElementType(context, f);

                if (returnType != string.Empty)
                {
                    returnType = string.Format("List<{0}>", returnType);
                }
            }

            if (returnType == string.Empty)
            {
                returnType = "void";
            }

            return returnType;
        }

        /// <summary>
        /// Get element type of store procedure function
        /// </summary>
        /// <param name="context"></param>
        /// <param name="f"></param>
        /// <returns></returns>
        public static string GetElementType(GeneratorContext context, Function f)
        {
            string returnType = string.Empty;

            if (f.ElementType != null)
            {
                if (string.IsNullOrEmpty(f.ElementType.Name) == false)
                {
                    returnType = f.ElementType.Name;
                }
                else if (string.IsNullOrEmpty(f.ElementType.IdRef) == false)
                {
                    Table refTable = context.DatabaseSchema.Tables.Find(
                            delegate(Table t)
                            {
                                return (t.DataType.Id == f.ElementType.IdRef);
                            });

                    if (refTable != null)
                    {
                        returnType = refTable.DataType.Name;
                    }
                }
            }

            return returnType;
        }

        /// <summary>
        /// Read content of a file
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static string ReadFromFile(string filePath)
        {
            StreamReader sr = null;
            string templateContent = string.Empty;
            try
            {
                sr = File.OpenText(filePath);

                if (sr != null)
                {
                    templateContent = sr.ReadToEnd();
                }
            }
            catch (System.Exception e)
            {
                throw new Exception("Can not read content of file: " + filePath + ". " + e.Message);
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                }
            }

            return templateContent;
        }

        /// <summary>
        /// Get one file with specific name in a directory and return its content
        /// </summary>
        /// <param name="dir"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static string ReadFileContent(DirectoryInfo dir, string fileName)
        {
            // Get entity mapper template file
            FileInfo[] arrEntityMapperTemplate = dir.GetFiles(fileName);

            // Get entity mapper template file content
            string fileContent = string.Empty;
            if (arrEntityMapperTemplate.Length > 0)
            {
                fileContent = ReadFromFile(arrEntityMapperTemplate[0].FullName);
            }

            return fileContent;
        }

        /// <summary>
        /// Write a text content to file
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="content"></param>
        public static void WriteToFile(string filePath, string content)
        {
            WriteToFile(filePath, content, false);
        }

        /// <summary>
        /// Write a text content to file
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="content"></param>
        /// <param name="createMode"></param>
        public static void WriteToFile(string filePath, string content, bool createMode)
        {
            // if createMode is true and file already exist, quit this method
            if (createMode && File.Exists(filePath))
            {
                return;
            }

            StreamWriter sw = null;
            try
            {
                sw = new StreamWriter(filePath, false);
                sw.Write(content);
            }
            catch (System.Exception e)
            {
                string message = string.Format("Fail to write content to file {0}. {1}", filePath, e.Message);
                throw new Exception(message);
            }
            finally
            {
                sw.Close();
            }
        }

        /// <summary>
        /// //Fill in template with property value
        /// </summary>
        /// <param name="setting"></param>
        /// <param name="template"></param>
        /// <param name="propertyTypes"></param>
        /// <returns></returns>
        public static string FillProperties(ProjectSetting setting, string template, params PropertyType[] propertyTypes)
        {
            string placeHolder = string.Empty;
            bool IsFillData = true;

            // fill in template value by retrieve all property setting
            foreach (PropertySetting property in setting.Properties)
            {
                // by default, if param is empty, fill all property type
                IsFillData = (propertyTypes.Length == 0) ? true : false;

                // other wise, just fill property with type in param
                for (int i = 0; i < propertyTypes.Length; i++)
                {
                    if (propertyTypes[i] == property.Type)
                    {
                        IsFillData = true;
                        break;
                    }
                }

                if (IsFillData)
                {
                    placeHolder = GeneratorHelper.GetPropertyPlaceHolder(property.Name, property.Type);
                    template = template.Replace(placeHolder, property.Value);
                }
            }

            return template;
        }

        /// <summary>
        /// Replace @ and $ by { and } in a tring
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public static string PlaceOpenClose(string code)
        {
            code = code.Replace("@", "{");
            code = code.Replace("$", "}");

            return code;
        }

        /// <summary>
        /// Check is data type is add-in datatype
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public static bool IsAddinDataType(string dataType)
        {
            if (dataType == Template.DotNetDataType.Boolean ||
                dataType == Template.DotNetDataType.Char ||
                dataType == Template.DotNetDataType.DateTime ||
                dataType == Template.DotNetDataType.Decimal ||
                dataType == Template.DotNetDataType.Double ||
                dataType == Template.DotNetDataType.Guid ||
                dataType == Template.DotNetDataType.Int16 ||
                dataType == Template.DotNetDataType.Int32 ||
                dataType == Template.DotNetDataType.Int64 ||
                dataType == Template.DotNetDataType.SByte ||
                dataType == Template.DotNetDataType.Single ||
                dataType == Template.DotNetDataType.String ||
                dataType == Template.DotNetDataType.UInt16 ||
                dataType == Template.DotNetDataType.UInt32 ||
                dataType == Template.DotNetDataType.UInt64)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Check whether data type is addin and nullable type
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        public static bool IsAddinNullableType(string dataType)
        {
            if (IsAddinDataType(dataType) == false)
            {
                return false;
            }

            if (dataType == Template.DotNetDataType.String)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Check if a file is not a override able file
        /// </summary>
        /// <param name="projectName"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static bool CheckIfOverridableFile(string projectName, string fileName)
        {
            bool overridable = true;
            string generateFileName = GetFileNameFromat(fileName);

            if (projectName == Template.Project.DataProjectName)
            {
                if (generateFileName == Template.DataProject.ProviderFileName)
                {
                    overridable = false;
                }
            }

            if (projectName == Template.Project.SqlProjectName)
            {
                if (generateFileName == Template.SqlProject.CustomStoredProcProviderFileName)
                {
                    overridable = false;
                }
            }

            return overridable;
        }

        /// <summary>
        /// Check if template file is special file. Special file is file not to be 
        /// generated one by one.
        /// [FileName]: File that will be generated to multi file
        /// &lt;MethodName&gt;: File contains function template
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static bool CheckIfSpecialFile(string fileName)
        {
            return (fileName.Contains("[") || fileName.Contains("<"));
        }
    }
}
